
------------------------------------------------------------------------
-- usb缓存处理
local usb_buf = {};
local insert = table.insert;
local format = string.format;
local usb_tran_func = function() end
local no_usb_func = true;
local debug_is_gw2ux = true;
local log_debug = function(f,...)
    local s = format(f,...);
    print(s);
end

log_debug = function() end

----------------------------------------------------------------------------
--usb数据调试
local jtag_state = 'RUNTEST';
local move_state = {};
move_state['TEST_LOGIC'] = {'RUNTEST','TEST_LOGIC'}
move_state['RUNTEST'] = {'RUNTEST','DR-SCAN'};
move_state['DR-SCAN'] = {'DR-CAP','IR-SCAN'};
move_state['IR-SCAN'] = {'IR-CAP','TEST_LOGIC'};
move_state['DR-CAP'] = {'DR-SHIFT','DR-EXIT1'};
move_state['IR-CAP'] = {'IR-SHIFT','IR-EXIT1'};
move_state['DR-SHIFT'] = {'DR-SHIFT','DR-EXIT1'};
move_state['IR-SHIFT'] = {'IR-SHIFT','IR-EXIT1'};
move_state['DR-EXIT1'] = {'DR-PAUSE','DR-UPDATE'};
move_state['IR-EXIT1'] = {'IR-PAUSE','IR-UPDATE'};
move_state['DR-PAUSE'] = {'DR-PAUSE','DR-EXIT2'};
move_state['IR-PAUSE'] = {'IR-PAUSE','IR-EXIT2'};
move_state['DR-EXIT2'] = {'DR-SHIFT','DR-UPDATE'};
move_state['IR-EXIT2'] = {'IR-SHIFT','IR-UPDATE'};
move_state['DR-UPDATE'] = {'RUNTEST','DR-SCAN'};
move_state['IR-UPDATE'] = {'RUNTEST','DR-SCAN'};

local debug_last_tms = 0;
local debug_last_tdi = 0;
local debug_tms_log = {};
local debug_tdi_log = {};
local debug_ir = ~0;
local debug_dr = ~0;
local debug_ir_pos = 0;
local debug_dr_pos = 0;
local debug_jtag = function(tms,tdi)
    -- insert(debug_tms_log,format('%d(%d,%x) ',tms,debug_dr_pos,debug_dr));
    -- insert(debug_tdi_log,format('%d(%d,%x) ',tdi,debug_dr_pos,debug_dr));

    log_debug('%s',jtag_state);
    insert(debug_tms_log,format('%d',tms));
    insert(debug_tdi_log,format('%d',tdi));

    if(jtag_state=='IR-SHIFT')then
        debug_ir = debug_ir + (tdi<<debug_ir_pos); debug_ir_pos = debug_ir_pos + 1;
    end
    if(jtag_state=='DR-SHIFT')then
        debug_dr = debug_dr + (tdi<<debug_dr_pos); debug_dr_pos = debug_dr_pos + 1;
    end

    if(jtag_state=='DR-UPDATE')then
        log_debug('tms log:%s',table.concat(debug_tms_log)); debug_tms_log = {jtag_state};
        log_debug('tdi log:%s',table.concat(debug_tdi_log)); debug_tdi_log = {jtag_state};
        log_debug('update dr:%x,%08x',debug_dr & 0x7,debug_dr>>3);
        debug_dr_pos = 0;
        debug_ir_pos = 0;
        debug_dr = 0;
    end

    if(jtag_state=='IR-UPDATE')then
        log_debug('tms log:%s',table.concat(debug_tms_log)); debug_tms_log = {jtag_state};
        log_debug('tdi log:%s',table.concat(debug_tdi_log)); debug_tdi_log = {jtag_state};
        log_debug('update ir:%08x',debug_ir);
        debug_ir_pos = 0;
        debug_dr_pos = 0;
        debug_ir = 0;
    end

    jtag_state = move_state[jtag_state][tms+1];

    debug_last_tms = tms;
    debug_last_tdi = tdi;
end
local gwu2x_cmd_len = {
    [0x5b] = 3,
    [0x5c] = 3,
    [0x6b] = 3,
    [0x6c] = 3,
    [0x6d] = 3,
    [0x6e] = 3,
    [0x9b] = 3,
    [0x9c] = 3,
    [0x9d] = 3,
    [0x8b] = 1,
    [0xdb] = 2,
    [0x20] = 3,
    [0x21] = 3,
    [0x22] = 1,
    [0x23] = 1,
    [0xcb] = 2,
};

local debug_tdi_bit_send = function(tdi_bit,bit_len)
    while(bit_len>=0)do
        bit_len = bit_len - 1;
        debug_jtag(debug_last_tms,tdi_bit & 1);
        tdi_bit = tdi_bit>>1;
    end
end

local gw2ux_usb_debug = function(usb_buf)
    local pos = 1;
    while(pos<#usb_buf)do
        local cmd = usb_buf[pos];
        log_debug('debug usb cmd:%02x %d %02x',cmd,usb_buf[pos+1],usb_buf[pos+2] or 0);
        if(cmd==0x5b or cmd==0x5c)then
            local bit_len = usb_buf[pos+1];
            local tms_bit = usb_buf[pos+2];
            local tdi = tms_bit>>7;

            while(bit_len>=0)do
                if(bit_len==0)then
                    -- 最后同时更新
                    debug_jtag(tms_bit&1,tdi);
                else
                    debug_jtag(tms_bit&1,debug_last_tdi);
                end
                tms_bit = tms_bit >> 1;
                bit_len = bit_len - 1;
            end
            pos = pos + 3;
        elseif(cmd==0x6b or cmd==0x6c)then
            --bit传输tdi
            local bit_len = usb_buf[pos+1];
            local tdi_bit = usb_buf[pos+2];
            debug_tdi_bit_send(tdi_bit,bit_len);
            pos = pos + 3;
        elseif(cmd==0x7b or cmd==0x7c)then
            --字节传输tdi
            local byt_len = usb_buf[pos+1];
            pos = pos + 2;
            while(byt_len>=0)do
                --字节传输
                debug_tdi_bit_send(usb_buf[pos],8-1);
                byt_len = byt_len - 1;
                pos = pos + 1;
            end
        elseif(cmd>=0x9b and cmd<=0x9d)then
            local bit_len = usb_buf[pos+1] + 1;
            bit_len = bit_len + usb_buf[pos+2]*0x100;
            while(bit_len>0)do
                bit_len = bit_len - 1;
                debug_jtag(debug_last_tms,debug_last_tdi);
            end
            pos = pos + 3;
        else
            --其他命令
            pos = pos + (gwu2x_cmd_len[cmd] or 1);
        end
    end
end

----------------------------------------------------------------------------

function usb_set_tran_func(func)
    usb_tran_func = func;
    no_usb_func = false;
end


local num2remote_bitbang = {};
for k=0,0xff do
    local a = {};
    local b = 1;
    while(b<0x100)do
        local v = '0';
        if((k&b)~=0)then
            v = '1';
        end
        insert(a,v);
        b = b << 1;
    end
    num2remote_bitbang[k]=table.concat(a);
end

local function remote_bitbang_send(d,tdo_len)
    if(not remote_bitbang_func)then return; end
    local s = {};
    for v in d:gmatch('.')do
        v = num2remote_bitbang[v:byte()];
        insert(s,v);
    end

    s = table.concat(s);
    s = s:sub(1,tdo_len);
    log_debug('recv:%d %s',tdo_len,s);
    remote_bitbang_func(s);
end

-------------------------------------------------------


local have_tdo = {};
local tdo_len_map = {};
local tdo_len = 0;
for k,v in ipairs({{0x5c,1},{0x6c,1},{0x7c,8},{0x9c,1}})do have_tdo[v[1]] = true; tdo_len_map[v[1]] = v[2]; end


local function usb_write_byte(byt)
    local d = byt & 0xff;

    --测试输出
    if(no_usb_func)then
        d = format('%02X',d);
    end

    insert(usb_buf,d);
end

local function usb_write_cmd_and_len(cmd,len)
    tdo_len = tdo_len + (tdo_len_map[cmd] or 0) * (len+1);
    usb_write_byte(cmd);
    usb_write_byte(len);
end

local function usb_write_tms_byte(tms,tdi)
    usb_write_byte((tms&0x7f)|(tdi<<7));
end

local function usb_write_tck_only(cmd,tck_tick)
    tdo_len = tdo_len + (tdo_len_map[cmd] or 0) * (tck_tick);
    tck_tick = tck_tick - 1;
    usb_write_byte(cmd);
    usb_write_byte(tck_tick);
    usb_write_byte(tck_tick>>8);
end

local j_state = "RUNTEST";

local function usb_flush()
    if((tdo_len%8)~=0)then
        usb_write_byte(0x8b);
    end

    if(no_usb_func)then
        local s = table.concat(usb_buf,',');
        log_debug('------------usb send:%s',s);
    end

    --调试命令
    log_debug('usb flush');
    if(debug_is_gw2ux)then
        gw2ux_usb_debug(usb_buf);
    end

    usb_tran_func(usb_buf);
    log_debug('usb flush end! tdo len=%d',tdo_len);

    if(j_state~=jtag_state)then
        log_debug('error state:%s %s',jtag_state,j_state);
        io.stdout:flush();
        os.exit();
    end

    --log_debug('tdo len:%d',tdo_len);
    if(tdo_len>0)then
        local d = usb_tran_func(nil,math.ceil(tdo_len/8) );
        --remote_bitbang(d);
        remote_bitbang_send(d,tdo_len);
        tdo_len = 0;
    end

    --初始化
    usb_buf = {};
end
------------------------------------------------------------------------

local last_tdi;
local last_tms;
local tdo_read = 0;

local tdi_change_notpull = false;
local tms_change_notpull = false;

local function buf_new(push_max)
    local buf = {value=0,pos=0,max=push_max or 8};
    function buf:push(bit,no_update)
        self.value = self.value | (bit<<self.pos);
        self.pos   = self.pos + 1;

        if(not no_update and (self.pos==self.max))then
            self:usb_write(last_tdi);
            self:init();
        end

    end
    function buf:have()
        return self.pos>0;
    end
    function buf:init()
        self.pos = 0;
        self.value = 0;
    end
    return buf;
end

local tdi_buf = buf_new(8);
local tms_buf = buf_new(7);

function tms_buf:usb_write(tdi)
    local bit_len = self.pos;
    if(bit_len>0)then
        log_debug('write tms:%d,%x',bit_len,self.value);
        usb_write_cmd_and_len(0x5b+tdo_read,bit_len-1);
        usb_write_tms_byte(self.value,tdi);
    end
    self:init();
end

function tms_buf:push_and_usb_write(tms,tdi)
    self:push(tms,true);
    self:usb_write(tdi);
end

function tdi_buf:usb_write()
    local byt_len = math.floor(self.pos / 8);
    local bit_len = self.pos % 8;

    log_debug('write tdi:%d %x',self.pos,self.value);

    if(byt_len>0)then
        --字节模式
        usb_write_cmd_and_len(0x7b+tdo_read,byt_len-1);
        while(byt_len>0)do
            usb_write_byte(self.value);
            self.value = self.value >> 8;
            byt_len = byt_len - 1;
        end
    end

    if(bit_len>0)then
        --位模式
        usb_write_cmd_and_len(0x6b+tdo_read,bit_len-1);
        usb_write_byte(self.value);
    end

    self:init();
end

local nochang_tick = 0;

local function nochang_check_and_update()
    if(nochang_tick>0)then
        assert(not tdi_buf:have());
        assert(not tms_buf:have());

        --只对tck处理
        -- usb_write_cmd_and_len(0x9b+tdo_read,nochang_tick);
        -- usb_write_byte(nochang_tick>>8);
        usb_write_tck_only(0x9b+tdo_read,nochang_tick);

        nochang_tick = 0;
    end
end

function j_flush()
    nochang_check_and_update();
    if(tms_buf:have())then
        assert(not tdi_buf:have());
        tms_buf:usb_write(last_tdi);
    end
    if(tdi_buf:have())then
        assert(not tms_buf:have());
        tdi_buf:usb_write();
    end
    usb_flush();
end

function j_write(tms,tdi)
    j_state = move_state[j_state][tms+1];
    log_debug('s:%s m:%d i:%d c:%d o:%d',j_state,tms,tdi,nochang_tick,tdo_len);

    --如果tms数据改变,tdi必须刷新

    --如果tms,tdi同时改变
    if(tms~=last_tms and tdi~=last_tdi)then
        nochang_check_and_update();
        if(tms_buf:have())then
            assert(not tdi_buf:have());
            tms_buf:push_and_usb_write(tms,tdi);
        else
            if(tdi_buf:have())then
                assert(not tms_buf:have());
                tdi_buf:usb_write();
                --最后一个数据,随tms推送
            end

            usb_write_cmd_and_len(0x5b+tdo_read,0);
            usb_write_tms_byte(tms,tdi);
        end

    elseif(tms~=last_tms)then
        --tms改变
        --如果tdi存在数据.则先更新tdi
        nochang_check_and_update();
        if(tdi_buf:have())then
            assert(not tms_buf:have());
            tdi_buf:usb_write();
        end

        tms_buf:push(tms);
    elseif(tdi~=last_tdi)then
        --tdi改变
        --如果tms存在数据
        nochang_check_and_update();
        if(tms_buf:have())then
            --tms存在数据,合并发送
            assert(not tdi_buf:have());
            tms_buf:push_and_usb_write(tms,tdi);
        else
            --tms为空,则直接往tdi发数据
            tdi_buf:push(tdi);
        end

    else
        --两个都不改变
        if(tms_buf:have())then
            tms_buf:push(tms);
        elseif(tdi_buf:have())then
            tdi_buf:push(tdi);
        else
            nochang_tick = nochang_tick + 1;
        end
    end


    last_tdi = tdi;
    last_tms = tms;
end

local num_map = {};
for k=0,7 do
    num_map[format('%d',k)] = k;
end

local getbit = function(v,b)
    return (v>>b) & 1;
end

local function scan(data)
    for tck in data:gmatch('[4567]')do
        tck = num_map[tck];
        local tdi,tms = getbit(tck,0),getbit(tck,1);
        j_write(tms,tdi);
    end
end


local last_data = '';

local function seq_process(data)
    --长度不足时,退出递归
    if(#data<=0)then return; end
    local last_d = data:sub(#data);

    if(last_d=='R')then
        last_data = last_d;
    end

    local tdo = data:match('^([0123]*R[01234567R_]-)[0123][4567]');
    if(not tdo and string.byte(data,1)==string.byte('R'))then
        tdo = data;
    end

    local no_tdo = data:match('^[^R]+');        --直到遇到R
    local next_data;
    if(tdo)then
        next_data = data:sub(#tdo+1);
        tdo_read = 1;
        tdo = tdo:gsub('R','');     --删除没有作用的
    else
        next_data = data:sub(#no_tdo+1);
        tdo_read = 0;
    end

    local seq = tdo or no_tdo;
    log_debug('seq(%d):r(%d),%s',#seq,tdo_read,seq);
    log_debug('rem:%s',next_data);
    scan(seq);
    j_flush();

    return seq_process(next_data);
end

function remote_bitbang(data)
    log_debug('====================================');
    seq_process(last_data .. data);
    j_flush();
end
